package com.katze.boot.plugins.redis.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.katze.boot.common.domain.GlobalConstant;
import com.katze.boot.plugins.redis.operations.RedisHashOperations;
import com.katze.boot.plugins.redis.operations.RedisOperations;
import com.katze.boot.plugins.redis.operations.RedisValueOperations;
import com.katze.boot.plugins.redis.operations.RedisZSetOperations;
import com.katze.boot.plugins.redis.service.CacheService;
import com.katze.boot.system.domain.User;
import com.katze.common.DatePattern;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service("cacheService")
public class CacheServiceImpl implements CacheService {
    private static final Logger log = LoggerFactory.getLogger(CacheServiceImpl.class);
    @Resource
    private ObjectMapper mapper;

    @Resource
    private RedisOperations<String> redisOperations;

    @Resource
    private RedisHashOperations<String> hashOperations;

    @Resource
    private RedisValueOperations<String> valueOperations;

    @Resource
    private RedisZSetOperations<String> zSetOperations;

    @Override
    public User saveUser(User user) {
        if (user == null) return null;
        return hashOperations.select(-1, operations -> {
            try {
                operations.put(GlobalConstant.USER_INFO_HASH_CACHE, user.getUsername(),mapper.writeValueAsString(user));
                return user;
            } catch (JsonProcessingException e) {
                log.error("用户对象转JSON格式异常", e);
                return null;
            }
        });
    }

    @Override
    public User getUser(String username, Function<String, User> databaseSelector) {
        try {
            String result = hashOperations.select(-1, operations -> operations.get(GlobalConstant.USER_INFO_HASH_CACHE, username));
            if (StringUtils.isBlank(result)){
                return saveUser(databaseSelector.apply(username));
            } else{
                return this.mapper.readValue(result, User.class);
            }
        } catch (JsonProcessingException e){
            log.error("JSON转用户对象格式异常", e);
            return null;
        }
    }


    @Override
    public void clearUserCache(String username) {
        hashOperations.select(-1, operations -> operations.delete(GlobalConstant.USER_INFO_HASH_CACHE, username));
    }

    @Override
    public String saveToken(String key, String value, long timeout) {
        return valueOperations.select(-1, operations -> {
            operations.set(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(key), value, timeout, TimeUnit.MILLISECONDS);
            return value;
        });
    }

    @Override
    public String getToken(String key) {
        return valueOperations.select(-1, operations -> operations.get(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(key)));
    }

    @Override
    public Boolean deleteToken(String key) {
        return redisOperations.select(-1, operations -> operations.delete(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(key)));
    }

    @Override
    public Boolean saveActiveUser(String value, double score){
        return zSetOperations.select(-1, operations -> operations.add(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, value, score));
    }

    @Override
    public Set<String> getAllActiveUser(){
        return zSetOperations.select(-1, operations -> {
            Set<String> sets = new HashSet<>();
            Cursor<ZSetOperations.TypedTuple<String>> cursor = operations.scan(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, ScanOptions.NONE);
            while (cursor.hasNext()){
                sets.add(cursor.next().getValue());
            }
            return sets;
        });
    }

    @Override
    public Long deleteActiveUser(String value){
        return zSetOperations.select(-1, operations -> operations.remove(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, value));
    }

    @Override
    public Long clearInvalidActiveUser(){
        String now = DatePattern.localDateTime(LocalDateTime.now().plusDays(-1));
        return zSetOperations.select(-1, operations -> operations.removeRangeByScore(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, 0, Double.parseDouble(now)));
    }

}
